//泛型(Generics General)是指在定义函数、接口和类的时候，不预先指定具体的类型
//而是在使用的时候再指定类型的一种特性

//泛型函数
/* 
function createArray<T>(length:number,value:T):T[]{
    let result:T[] = [];
    for(let i=0;i<length;i++){
        result[i]=value;
    }
    return result;
}
let result = createArray<string>(3,'a');
console.log(result) 
*/

//类数组
//类数组(Array Like Object)不是数组类型，但是长的很像数组
//属性都是数字索引
/* function sum(...numbers:number[]){
    console.log(Array.isArray(numbers))
    console.log(Array.isArray(arguments))
    let args:IArguments = arguments;
    for(let i=0;i<args.length;i++){
        console.log(args[i])
    }
}
//应有 0 个参数，但获得 3 个
sum(1,2,3);

let children:HTMLCollection = document.getElementById('root').children;
let nodeList:NodeListOf<ChildNode> = document.getElementById('root').childNodes;
 */

//可以在类中使用泛型
/* class MyArray<T>{
    private list:Array<T> = []//定义数组的元素类型
    add(val:T){//定义参数类型
        this.list.push(val);
    }
    getMax():T{//定义返回值类型
        let result:T = this.list[0];
        for(let i=0;i<this.list.length;i++){
            if(this.list[i]>result){
                result = this.list[i];
            }
        }
        return result;
    }
}
let arr = new MyArray();
arr.add(1);arr.add(2);arr.add(3);
let result = arr.getMax();
console.log(result) */

//在接口中也可以使用泛型
//泛型接口可以用来约束函数
/**
不能将类型“<T>(a: number, b: number) => number”分配给类型“Calculate”。
  参数“a”和“a” 的类型不兼容。
    不能将类型“T”分配给类型“number”。ts(2322) 
 */

/* 
interface Calculate<T>{
    (a:T,b:T):T
}

let sum:Calculate<number>= function(a:number,b:number):number{
    return a+b;
} */

/* interface Calculate{
    <T>(a:T,b:T):T
}
let sum:Calculate= function<T>(a:T,b:T):T{
    return a;
}
//因为sum函数接收泛型T，在调用sum函数的时候可以传递真正的类型
sum<number>(1,2); */


//多个泛型参数
/* function swap<A,B>(tuple:[A,B]):[B,A]{
    return [tuple[1],tuple[0]];
}
let result:[number, string] = swap<string,number>(['a',1]); */

//默认泛型参数
/* function createArray<T=number>(length:number,value:T):Array<T>{
    let result:Array<T> = [];
    for(let i=0;i<length;i++){
        result[i]=value;
    }
    return result
}
let result:number[] = createArray(3,1); */
//泛型约束
//在函数中使用泛型的时候，由于事先不知道它是什么类型，所以不能随意访问相应类型的属性和方法

/* function logger<T>(val:T){
    console.log(val.length)
}
logger<string>('hello') */
interface LengthWise{
    length:number
}
//对泛型T进行约束，要求它扩展自LengthWise,也就是说必须满足 LengthWise的要求
function logger<T extends LengthWise>(val:T){
    console.log(val.length)
}
//类型“number”不满足约束“LengthWise”
logger<Array<number>>([1,2,3])

//泛型类型的别名
//泛型类型的别名可以表达更复杂的类型
type Cart<T> = {list:T[]}|T[];
let c1:Cart<string> = {list:['1','2','3']}
let c3:Cart<string> = ['1','2','3']
let c2:Cart<number> = [1,2,3]

/**
 * 泛型接口 VS  泛型类型别名
 * interface    type
 * 接口是真正创建一个类型的名称，可以在任何地方调用
 * 而类型别名并不会创建新的类型名称，比如报错的时候就不能类型别名
 * 类型别名不能被 extends 或者 说implements实现。尽可能使用interface
 * 如果需要使用联合类型的话，可以使用类型别名 
 */